package com.easyapi.server.service.user.impl;

import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easyapi.common.base.UserTokenThreadHolder;
import com.easyapi.common.contants.Constants;
import com.easyapi.common.enums.StatusCode;
import com.easyapi.common.enums.user.UserOperationActions;
import com.easyapi.common.enums.user.UserRole;
import com.easyapi.common.enums.user.UserStatus;
import com.easyapi.common.exception.BizException;
import com.easyapi.common.param.user.UserPageParam;
import com.easyapi.common.utils.RegularUtil;
import com.easyapi.common.vo.user.ApiCenterUserVo;
import com.easyapi.common.vo.user.PermissionVo;
import com.easyapi.dao.entity.user.ApiCenterUser;
import com.easyapi.dao.mapper.user.ApiCenterUserMapper;
import com.easyapi.server.service.captcha.CaptchaService;
import com.easyapi.server.service.user.RoleService;
import com.easyapi.server.service.user.UserService;
import com.easyapi.server.utils.JWTUtil;
import com.easyapi.server.utils.RedisCache;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @author javadadi
 */
@Service
public class UserServiceImpl extends ServiceImpl<ApiCenterUserMapper, ApiCenterUser> implements UserService {
    @Resource
    private RedisCache redisCache;
    @Resource
    private ApiCenterUserMapper userMapper;
    @Resource
    private CaptchaService captchaService;
    @Resource
    private RoleService roleService;

    private ReentrantLock lock = new ReentrantLock();
    /* using by controller*/

    /**
     * 用户登陆
     *
     * @param response     响应
     * @param userAccount  用户账户
     * @param userPassword 用户密码
     * @param uuid         uuid
     * @param code         验证码
     * @return true
     */
    @Override
    public Boolean login(HttpServletResponse response, HttpServletRequest request, String userAccount, String userPassword, String uuid, String code) {
        //前置校验
        ApiCenterUser user = checkPreLogin(userAccount, userPassword);
        //验证码校验
        captchaService.validateCaptcha(uuid, code);
        //密码校验
        checkUserPassword(user, userPassword);
        //登陆成功-清除设置的key
        String redisKey = Constants.PASSWORD_ERROR + user.getId();
        redisCache.del(uuid, redisKey);
        //查询用户的角色
        List<PermissionVo> permission = roleService.getPermissionByUserId(user.getId());
        List<String> perName = permission.stream().map(PermissionVo::getName).collect(Collectors.toList());
        List<String> roles = roleService.getRolesByUserId(user.getId());
        ApiCenterUserVo userVo = ApiCenterUserVo.builder()
                .id(user.getId())
                .userName(user.getUserName())
                .userAccount(user.getUserAccount())
                .userEmail(user.getUserEmail())
                .gender(user.getGender())
                .userAvatar(user.getUserAvatar())
                .userStatues(user.getUserStatues())
                .appKey(user.getAppKey())
                .appSecret(user.getAppSecret())
                .permissions(perName)
                .role(roles)
                .build();
        //设置登陆标识
        String userVoJson = JSONUtil.toJsonStr(userVo);
        Map<String, String> map = new HashMap<>();
        String ip = request.getRemoteAddr();
        map.put("userAccount", userVo.getUserAccount());
        map.put("userId", String.valueOf(userVo.getId()));
        //登陆信息绑定ip
        map.put("ip", ip);
        String token = JWTUtil.sign(map);
        redisCache.set(Constants.SYSTEM_USING + userVo.getId() + userVo.getUserAccount(), userVoJson);
        response.setHeader("token", token);
        return true;
    }

    /**
     * 用户注册
     *
     * @param userAccount       用户账户
     * @param userPassword      用户密码
     * @param userPasswordCheck 用户密码确认
     * @return true
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean register(String userAccount, String userPassword, String userPasswordCheck) {
        //前置校验
        checkPerRegister(userAccount, userPassword, userPasswordCheck);
        //先保存起来
        ApiCenterUser user = new ApiCenterUser();
        user.setUserAccount(userAccount);
        String currentPassword = DigestUtil.md5Hex((Constants.PASSWORD_SLOT + userPassword).getBytes());
        user.setUserPassword(currentPassword);
        //生成ak/sk

        userMapper.insert(user);
        //保存默认的角色
        roleService.saveDefaultRole(user);
        return true;
    }

    /**
     * 用户操作
     *
     * @param userId 用户id
     * @param action 操作类型
     * @param roleId 角色id
     * @return true
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean userOperation(Long userId, String action, Long roleId) {
        if (StringUtils.isEmpty(action)) {
            throw new BizException(StatusCode.PARAMS_ERROR);
        }
        //用户禁用
        if (Objects.equals(action, UserOperationActions.BIND_USER.getAction())) {
            updateUserStatus(UserOperationActions.BIND_USER.getCoed(), userId);
            return true;
        }
        //用户解禁
        if (Objects.equals(action, UserOperationActions.LIFT_USER.getAction())) {
            updateUserStatus(UserOperationActions.LIFT_USER.getCoed(), userId);
            return true;
        }
        //用户授权
        if (Objects.equals(action, UserOperationActions.EMPOWER_USER.getAction())) {
            if (!Objects.isNull(roleId)) {
                roleService.addRoleUsersByRoleIdAndUserId(roleId, userId);
            }
            return true;
        }
        //用户取消授权
        if (Objects.equals(action, UserOperationActions.REVOKE_USER.getAction())) {
            if (!Objects.isNull(roleId)) {
                roleService.delRoleUsersRoleIdAndUserId(roleId, userId);
            }
            return true;
        }
        throw new BizException(StatusCode.OPERATION_ERROR);
    }

    /**
     * 退出登陆
     *
     * @param response 响应
     * @return true
     */
    @Override
    public Boolean loginOut(HttpServletResponse response) {
        ApiCenterUserVo currentUser = UserTokenThreadHolder.getCurrentUser();
        if (Objects.isNull(currentUser)) {
            throw new BizException(StatusCode.NO_LOGIN);
        }
        //删除redis相关
        if (redisCache.hasKey(Constants.SYSTEM_USING + currentUser.getUserAccount() + currentUser.getId())) {
            redisCache.del(Constants.SYSTEM_USING + currentUser.getUserAccount() + currentUser.getId());
        }
        response.setHeader("token", "");
        return true;
    }

    /**
     * 分页
     *
     * @param param 分页参数
     *              <p>参数详情</p>
     *              <p>id 用户id</p>
     *              <p>userName 用户名</p>
     *              <p>userAccount 用户账户</p>
     *              <p>userEmail 用户邮件</p>
     *              <p>gender 用户性别</p>
     *              <p>userStatus 用户状态 0-正常 1-禁用 2-异常</p>
     */
    @Override
    public Page<ApiCenterUserVo> page(UserPageParam param) {
        Long id = param.getId();
        String userName = param.getUserName();
        String userAccount = param.getUserAccount();
        String userEmail = param.getUserEmail();
        Integer gender = param.getGender();
        Integer userStatues = param.getUserStatues();
        Long size = param.getSize();
        Long current = param.getCurrent();
        LambdaQueryWrapper<ApiCenterUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(!Objects.isNull(id), ApiCenterUser::getId, id)
                .eq(Objects.nonNull(userStatues), ApiCenterUser::getUserStatues, userStatues)
                .eq(Objects.nonNull(gender), ApiCenterUser::getGender, gender)
                .like(StringUtils.isNotEmpty(userAccount), ApiCenterUser::getUserAccount, userAccount)
                .like(StringUtils.isNotEmpty(userName), ApiCenterUser::getUserName, userName)
                .eq(StringUtils.isNotEmpty(userEmail), ApiCenterUser::getUserEmail, userEmail);
        Page<ApiCenterUser> page = this.page(new Page<>(current, size), wrapper);
        Page<ApiCenterUserVo> voPage = new Page<>();
        voPage.setSize(size);
        voPage.setCurrent(current);
        voPage.setTotal(page.getTotal());
        List<ApiCenterUserVo> userVos = page.getRecords().stream().map(m -> {
            ApiCenterUserVo userVo = new ApiCenterUserVo();
            BeanUtils.copyProperties(m, userVo);
            return userVo;
        }).collect(Collectors.toList());
        voPage.setRecords(userVos);
        return voPage;
    }

    /**
     * 用户详情获取
     * @param id 用户id
     * @return ApiCenterUserVo
     */
    @Override
    public ApiCenterUserVo getUserById(Long id) {
        ApiCenterUser user = getById(id);
        if (Objects.isNull(user)) {
            throw new BizException(StatusCode.NO_SUCH_USER);
        }
        //拿到用户的所有角色
        List<String> roles = roleService.getRolesByUserId(id);
        //拿到所有权限
        List<String> permissions = roleService.getPermissionByUserId(id).stream().map(PermissionVo::getDisplay).collect(Collectors.toList());
        return ApiCenterUserVo.builder()
                .id(user.getId())
                .userName(user.getUserAccount())
                .userAccount(user.getUserAccount())
                .userEmail(user.getUserEmail())
                .gender(user.getGender())
                .userAvatar(user.getUserAvatar())
                .userStatues(user.getUserStatues())
                .createAt(user.getCreateAt())
                .updateAt(user.getUpdateAt())
                .role(roles)
                .permissions(permissions)
                .build();
    }

    /*function--method*/

    /**
     * 更新用户状态
     * @param code 状态码
     * @param id 用户id
     */
    public void updateUserStatus(Integer code, Long id) {
        if (Objects.isNull(code) || Objects.isNull(id)) {
            throw new BizException(StatusCode.PARAMS_ERROR);
        }
        //管理员能够更改用户状态
        ApiCenterUserVo currentUser = UserTokenThreadHolder.getCurrentUser();
        boolean isAdmin = currentUser.getRole().contains(UserRole.ADMIN.getRoleName());
        if (BooleanUtils.isFalse(isAdmin)) {
            throw new BizException(StatusCode.NO_AUTH_ERROR);
        }
        ApiCenterUser user = userMapper.selectById(id);
        if (Objects.isNull(user)) {
            throw new BizException(StatusCode.NO_SUCH_USER);
        }
        user.setUserStatues(code);
        userMapper.updateById(user);
    }

    /**
     * 用户信息检验--注册
     * @param userAccount 用户账户
     * @param userPassword 用户密码
     * @param userPasswordCheck 用户密码确认
     */
    public void checkPerRegister(String userAccount, String userPassword, String userPasswordCheck) {
        boolean account = RegularUtil.isAccount(userAccount);
        boolean password = RegularUtil.isPassword(userPassword);
        boolean passwordCheck = RegularUtil.isPassword(userPasswordCheck);
        if (BooleanUtils.isFalse(account && passwordCheck && password)) {
            throw new BizException(StatusCode.FORMAT_ERROR);
        }
        ApiCenterUser user = userMapper.getByUserAccount(userAccount);
        if (Objects.nonNull(user)) {
            throw new BizException(StatusCode.EXISTING_USER);
        }
    }

    /**
     * 用户登陆校验
     * @param userAccount 用户账户
     * @param userPassword 用户密码
     * @return boolean
     */
    private ApiCenterUser checkPreLogin(String userAccount, String userPassword) {
        boolean account = RegularUtil.isAccount(userAccount);
        boolean password = RegularUtil.isPassword(userPassword);
        if (BooleanUtils.isFalse(account && password)) {
            throw new BizException(StatusCode.PARAMS_ERROR);
        }
        ApiCenterUser user = userMapper.getByUserAccount(userAccount);
        if (Objects.isNull(user)) {
            throw new BizException(StatusCode.NO_SUCH_USER);
        }
        Integer userStatues = user.getUserStatues();
        if (Objects.equals(userStatues, UserStatus.BIND_USER.getCode())) {
            throw new BizException(UserStatus.BIND_USER.getStatus());
        }
        if (Objects.equals(userStatues, UserStatus.EXCEPTION_USER.getCode())) {
            throw new BizException(UserStatus.EXCEPTION_USER.getStatus());
        }
        return user;
    }

    /**
     * 用户密码校验
     * @param user 用户信息
     * @param password 用户密码
     */
    private void checkUserPassword(ApiCenterUser user, String password) {
        String redisKey = Constants.PASSWORD_ERROR + user.getId()+user.getUserAccount();
        boolean hasKey = redisCache.hasKey(redisKey);
        String currentPassword = DigestUtil.md5Hex((Constants.PASSWORD_SLOT + password).getBytes());
        if (hasKey) {
            //查看错误次数
            int count = Integer.parseInt(redisCache.get(redisKey).toString());
            if (count >= 5) {
                long l = count / 5;
                long expire = redisCache.getExpire(redisKey);
                redisCache.set(redisKey, count, l * Constants.PASSWORD_ERROR_EXPIRE_TIME);
                throw new BizException(String.format("密码错误次数过多,请%d秒后再次尝试", expire));
            }
            //密码校验
            if (!Objects.equals(currentPassword, user.getUserPassword())) {
                redisCache.incr(redisKey, 1L);
                throw new BizException(StatusCode.PASSWORD_ERROR);
            }
        } else {
            if (!Objects.equals(currentPassword, user.getUserPassword())) {
                redisCache.set(redisKey, 1L, -1);
                throw new BizException(StatusCode.PASSWORD_ERROR);
            }
        }
    }

}
